﻿using System;
using System.Collections.Generic;
using System.Linq;
using LinqLib.Sequence;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NetAssist.TruthTable;

namespace NetAssistTests
{
  [TestClass]
  public class TruthTableTests
  {
    private static ulong[] pow2 = Enumerator.Generate(0, 64, i => (ulong)Math.Pow(2, i)).ToArray();

    [TestMethod()]
    public void TruthTableItemTest()
    {
      TruthTableItem tti = new TruthTableItem(5, 4);
      Assert.IsTrue(tti.ToString() == "0101");
      Assert.IsTrue(tti.ToBoolExpression() == "aBcD");
      Assert.IsTrue(tti.Equals(null) == false);
    }

    [TestMethod()]
    public void TruthTableTest()
    {
      bool[] tt = new bool[15];
      Table tbl;

      try
      {
        tbl = new Table(tt);
      }
      catch (ArgumentException e) { Assert.IsTrue(e.ParamName == "minTerms"); }
      catch (Exception) { Assert.Fail(); }

      tt = new bool[8];
      tt[0] = true;
      tt[1] = true;
      tt[2] = true;
      tt[3] = true;
      tt[4] = false;
      tt[5] = false;
      tt[6] = true;
      tt[7] = false;

      tbl = new Table(tt);
      Assert.IsTrue(tbl.Inputs == 3);
      Assert.IsTrue(tbl[2].ToString() == "010");
      Assert.IsTrue(tbl.GetTruthTableExpression().ToString() == "((((((Not(inputA) AndAlso Not(inputB)) AndAlso Not(inputC)) OrElse ((inputA AndAlso Not(inputB)) AndAlso Not(inputC))) OrElse ((Not(inputA) AndAlso inputB) AndAlso Not(inputC))) OrElse ((inputA AndAlso inputB) AndAlso Not(inputC))) OrElse ((Not(inputA) AndAlso inputB) AndAlso inputC))");
      Assert.IsTrue(tbl.ToString() == "abc || abC || aBc || aBC || ABc");

      tbl.Simplify();
      Assert.IsTrue(tbl[1].ToString() == "X10");
      Assert.IsTrue(tbl.GetTruthTableExpression().ToString() == "(Not(inputC) OrElse (Not(inputA) AndAlso inputB))");
      Assert.IsTrue(tbl.ToString() == "a || Bc");

      tt = new bool[8];
      tbl = new Table(tt);
      tbl.Simplify();
      Assert.IsTrue(tbl.ToString() == "0");

      tt = new bool[] { true, true, true, true, true, true, true, true };
      tbl = new Table(tt);
      tbl.Simplify();
      Assert.IsTrue(tbl.ToString() == "1");
    }

    [TestMethod()]
    public void TruthTableTest2()
    {
      bool[] tt = new bool[4];
      bool[] vals = new bool[2];

      int start = 0;
      int end = (int)Math.Pow(2, 4) - 1;

      foreach (int value in Enumerator.Range(start, 1, end))
      {
        tt[0] = (value & 1) == 1;
        tt[1] = (value & 2) == 2;
        tt[2] = (value & 4) == 4;
        tt[3] = (value & 8) == 8;

        Table t = new Table(tt);
        var rawExp = (Func<bool, bool, bool>)t.GetTruthTableDelegate();
        t.Simplify();
        var newExp = (Func<bool, bool, bool>)t.GetTruthTableDelegate();
        for (int j = 0; j < 4; j++)
        {
          vals[0] = (j & 1) == 1;
          vals[1] = (j & 2) == 2;
          Assert.IsTrue(newExp(vals[0], vals[1]) == rawExp(vals[0], vals[1]));
        }
      }
    }

    [TestMethod()]
    public void TruthTableTest2a()
    {
      bool[] tt = new bool[4];
      bool[] inpA = new bool[4];
      bool[] inpB = new bool[4];
      IEnumerable<bool> ouptput;

      int start = 0;
      int end = (int)Math.Pow(2, 4) - 1;

      for (int j = 0; j < 4; j++)
      {
        inpA[j] = (j & 1) == 1;
        inpB[j] = (j & 2) == 2;
      }

      foreach (uint value in Enumerator.Range(start, 1, end))
      {
        for (int i = 0; i < 4; i++)
          tt[i] = (value & pow2[i]) == pow2[i];

        Table t = new Table(tt);
        t.Simplify();
        ouptput = tt.Evaluate(inpA, inpB);
        Assert.IsTrue(ouptput.SequenceEqual(tt));
      }

      try
      {
        var temp = tt.Concat(tt).Evaluate(inpA, inpB).ToArray();
        Assert.Fail();
      }
      catch (ArgumentException e) { Assert.IsTrue(e.ParamName == "truthTable"); }
      catch (Exception) { Assert.Fail(); }

    }

    [TestMethod()]
    public void TruthTableTest3()
    {
      bool[] tt = new bool[8];
      bool[] vals = new bool[3];

      int start = 0;
      int end = (int)Math.Pow(2, 8) - 1;

      foreach (uint value in Enumerator.Range(start, 1, end))
      {
        for (int i = 0; i < 8; i++)
          tt[i] = (value & pow2[i]) == pow2[i];

        Table t = new Table(tt);
        var rawExp = (Func<bool, bool, bool, bool>)t.GetTruthTableDelegate();
        t.Simplify();
        var newExp = (Func<bool, bool, bool, bool>)t.GetTruthTableDelegate();
        for (int j = 0; j < 8; j++)
        {
          vals[0] = (j & 1) == 1;
          vals[1] = (j & 2) == 2;
          vals[2] = (j & 4) == 4;
          Assert.IsTrue(newExp(vals[0], vals[1], vals[2]) == rawExp(vals[0], vals[1], vals[2]));
        }
      }
    }

    [TestMethod()]
    public void TruthTableTest3a()
    {
      bool[] tt = new bool[8];
      bool[] inpA = new bool[8];
      bool[] inpB = new bool[8];
      bool[] inpC = new bool[8];
      IEnumerable<bool> ouptput;

      int start = 0;
      int end = (int)Math.Pow(2, 8) - 1;

      for (int j = 0; j < 8; j++)
      {
        inpA[j] = (j & 1) == 1;
        inpB[j] = (j & 2) == 2;
        inpC[j] = (j & 4) == 4;
      }

      foreach (uint value in Enumerator.Range(start, 1, end))
      {
        for (int i = 0; i < 8; i++)
          tt[i] = (value & pow2[i]) == pow2[i];

        Table t = new Table(tt);
        t.Simplify();
        ouptput = tt.Evaluate(inpA, inpB, inpC);
        Assert.IsTrue(ouptput.SequenceEqual(tt));
      }

      try
      {
        var temp = tt.Concat(tt).Evaluate(inpA, inpB, inpC).ToArray();
        Assert.Fail();
      }
      catch (ArgumentException e) { Assert.IsTrue(e.ParamName == "truthTable"); }
      catch (Exception) { Assert.Fail(); }
    }

    [TestMethod()]
    public void TruthTableTest4()
    {
      bool[] tt = new bool[16];
      bool[] vals = new bool[4];

      int start = 0;
      int end = (int)Math.Pow(2, 16) - 1;

      foreach (uint value in Enumerator.Range(start, 1, end).Shuffle().Take(1000).Concat(new int[] { start, end }))
      {
        for (int i = 0; i < 16; i++)
          tt[i] = (value & pow2[i]) == pow2[i];

        Table t = new Table(tt);
        var rawExp = (Func<bool, bool, bool, bool, bool>)t.GetTruthTableDelegate();
        t.Simplify();
        var newExp = (Func<bool, bool, bool, bool, bool>)t.GetTruthTableDelegate();
        for (int j = 0; j < 16; j++)
        {
          vals[0] = (j & 1) == 1;
          vals[1] = (j & 2) == 2;
          vals[2] = (j & 4) == 4;
          vals[3] = (j & 8) == 8;
          Assert.IsTrue(newExp(vals[0], vals[1], vals[2], vals[3]) == rawExp(vals[0], vals[1], vals[2], vals[3]));
        }
      }
    }

    [TestMethod()]
    public void TruthTableTest4a()
    {
      bool[] tt = new bool[16];
      bool[] inpA = new bool[16];
      bool[] inpB = new bool[16];
      bool[] inpC = new bool[16];
      bool[] inpD = new bool[16];
      IEnumerable<bool> ouptput;

      int start = 0;
      int end = (int)Math.Pow(2, 16) - 1;

      for (int j = 0; j < 16; j++)
      {
        inpA[j] = (j & 1) == 1;
        inpB[j] = (j & 2) == 2;
        inpC[j] = (j & 4) == 4;
        inpD[j] = (j & 8) == 8;
      }

      foreach (uint value in Enumerator.Range(start, 1, end).Shuffle().Take(1000))
      {
        for (int i = 0; i < 16; i++)
          tt[i] = (value & pow2[i]) == pow2[i];

        Table t = new Table(tt);
        t.Simplify();
        ouptput = tt.Evaluate(inpA, inpB, inpC, inpD);
        Assert.IsTrue(ouptput.SequenceEqual(tt));
      }

      try
      {
        var temp = tt.Concat(tt).Evaluate(inpA, inpB, inpC, inpD).ToArray();
        Assert.Fail();
      }
      catch (ArgumentException e) { Assert.IsTrue(e.ParamName == "truthTable"); }
      catch (Exception) { Assert.Fail(); }
    }

    [TestMethod()]
    public void TruthTableTest5()
    {
      bool[] tt = new bool[32];
      bool[] vals = new bool[5];

      int start = 0;
      int end = (int)Math.Pow(2, 16) - 1;

      foreach (uint value in Enumerator.Range(start, 1, end).Shuffle().Take(1000).Concat(new int[] { start, int.MinValue }))
      {
        for (int i = 0; i < 31; i++)
          tt[i] = (value & pow2[i]) == pow2[i];

        Table t = new Table(tt);
        var rawExp = (Func<bool, bool, bool, bool, bool, bool>)t.GetTruthTableDelegate();
        t.Simplify();
        var newExp = (Func<bool, bool, bool, bool, bool, bool>)t.GetTruthTableDelegate();
        for (int j = 0; j < 32; j++)
        {
          vals[0] = (j & 1) == 1;
          vals[1] = (j & 2) == 2;
          vals[2] = (j & 4) == 4;
          vals[3] = (j & 8) == 8;
          vals[4] = (j & 16) == 16;
          Assert.IsTrue(newExp(vals[0], vals[1], vals[2], vals[3], vals[4]) == rawExp(vals[0], vals[1], vals[2], vals[3], vals[4]));
        }
      }
    }

    [TestMethod()]
    public void TruthTableTest5a()
    {
      bool[] tt = new bool[32];
      bool[] inpA = new bool[32];
      bool[] inpB = new bool[32];
      bool[] inpC = new bool[32];
      bool[] inpD = new bool[32];
      bool[] inpE = new bool[32];
      IEnumerable<bool> ouptput;

      int start = 0;
      int end = (int)Math.Pow(2, 16) - 1;

      for (int j = 0; j < 32; j++)
      {
        inpA[j] = (j & 1) == 1;
        inpB[j] = (j & 2) == 2;
        inpC[j] = (j & 4) == 4;
        inpD[j] = (j & 8) == 8;
        inpE[j] = (j & 16) == 16;
      }

      foreach (uint value in Enumerator.Range(start, 1, end).Shuffle().Take(1000).Concat(new int[] { start, int.MinValue }))
      {
        for (int i = 0; i < 32; i++)
          tt[i] = (value & pow2[i]) == pow2[i];

        Table t = new Table(tt);
        t.Simplify();
        ouptput = tt.Evaluate(inpA, inpB, inpC, inpD, inpE);
        Assert.IsTrue(ouptput.SequenceEqual(tt));
      }

      try
      {
        var temp = tt.Concat(tt).Evaluate(inpA, inpB, inpC, inpD, inpE).ToArray();
        Assert.Fail();
      }
      catch (ArgumentException e) { Assert.IsTrue(e.ParamName == "truthTable"); }
      catch (Exception) { Assert.Fail(); }
    }

    [TestMethod()]
    public void TruthTableTest6()
    {
      bool[] tt = new bool[64];
      bool[] vals = new bool[6];

      long start = 0;
      long end = (long)Math.Pow(2, 16) - 1;

      foreach (uint value in Enumerator.Range(start, 1, end).Shuffle().Take(1000).Concat(new long[] { start, long.MinValue }))
      {
        for (int i = 0; i < 63; i++)
          tt[i] = (value & pow2[i]) == pow2[i];

        Table t = new Table(tt);
        var rawExp = (Func<bool, bool, bool, bool, bool, bool, bool>)t.GetTruthTableDelegate();
        t.Simplify();
        var newExp = (Func<bool, bool, bool, bool, bool, bool, bool>)t.GetTruthTableDelegate();
        for (int j = 0; j < 64; j++)
        {
          vals[0] = (j & 1) == 1;
          vals[1] = (j & 2) == 2;
          vals[2] = (j & 4) == 4;
          vals[3] = (j & 8) == 8;
          vals[4] = (j & 16) == 16;
          vals[5] = (j & 32) == 32;
          Assert.IsTrue(newExp(vals[0], vals[1], vals[2], vals[3], vals[4], vals[5]) == rawExp(vals[0], vals[1], vals[2], vals[3], vals[4], vals[5]));
        }
      }
    }

    [TestMethod()]
    public void TruthTableTest6a()
    {
      bool[] tt = new bool[64];
      bool[] inpA = new bool[64];
      bool[] inpB = new bool[64];
      bool[] inpC = new bool[64];
      bool[] inpD = new bool[64];
      bool[] inpE = new bool[64];
      bool[] inpF = new bool[64];
      IEnumerable<bool> ouptput;

      long start = 0;
      long end = (int)Math.Pow(2, 16) - 1;

      for (int j = 0; j < 64; j++)
      {
        inpA[j] = (j & 1) == 1;
        inpB[j] = (j & 2) == 2;
        inpC[j] = (j & 4) == 4;
        inpD[j] = (j & 8) == 8;
        inpE[j] = (j & 16) == 16;
        inpF[j] = (j & 32) == 32;
      }

      foreach (ulong value in Enumerator.Range(start, 1, end).Shuffle().Take(1000).Concat(new long[] { start, long.MinValue }))
      {
        for (int i = 0; i < 64; i++)
          tt[i] = (value & pow2[i]) == pow2[i];

        Table t = new Table(tt);
        t.Simplify();
        ouptput = tt.Evaluate(inpA, inpB, inpC, inpD, inpE, inpF);
        Assert.IsTrue(ouptput.SequenceEqual(tt));
      }

      try
      {
        var temp = tt.Concat(tt).Evaluate(inpA, inpB, inpC, inpD, inpE, inpF).ToArray();
        Assert.Fail();
      }
      catch (ArgumentException e) { Assert.IsTrue(e.ParamName == "truthTable"); }
      catch (Exception) { Assert.Fail(); }
    }
  }
}
